package pilhun.collections;

import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.RandomAccess;



public class MyArrayList implements MyList, RandomAccess{
	
	private int size = 0;
	private int capacity;
	private int globalCounter=0;
	private Object[] innerArray;
	
	public MyArrayList(){
		innerArray = new Object[10];
		capacity = 10;
	}
	public	MyArrayList(int initialCapacity){
		innerArray = new Object[initialCapacity];
		capacity = initialCapacity;
	}
	public	MyArrayList(MyList c){
		innerArray = c.toArray(); 
	}
	
	private class IteratorImpl implements Iterator{
		
		private int ind;
		private int lastReturned = -1;
		private int counter;
		
		public IteratorImpl() {
			setIndex(0);
			setCounter(globalCounter);
		}

		public boolean hasNext() {
			if (globalCounter != getCounter()){
				throw new ConcurrentModificationException();
			}
			if (nextIndex()>=size){
				return false;
			}
			return true;
		}
		
		protected boolean hasPrevious(){
			if (globalCounter != getCounter()){
				throw new ConcurrentModificationException();
			}
			if (previousIndex()<0){
				return false;
			}
			return true;
		}

		public Object next() throws NoSuchElementException{

			if (globalCounter != getCounter()){
				throw new ConcurrentModificationException();
			}
			if (!hasNext()){
				throw new NoSuchElementException();
			}
			Object obj = innerArray[nextIndex()];
			setLastReturned(nextIndex());
			setIndex(nextIndex()+1);
			return obj;
		}
		
		protected Object previous(){
			if (globalCounter != getCounter()){
				throw new ConcurrentModificationException();
			}
			if (!hasPrevious()){
				throw new NoSuchElementException();
			}
			Object obj = innerArray[previousIndex()];
			setLastReturned(previousIndex());
			setIndex(nextIndex()-1);
			return obj;
		}
		
		public void remove() {
			if (globalCounter != getCounter()){
				throw new ConcurrentModificationException();
			}
			if (getLastReturned() == -1){
				throw new IllegalStateException();
			}
			
			int tailLen = size - getLastReturned() - 1;
			System.arraycopy(innerArray, getLastReturned() + 1, innerArray, getLastReturned(), tailLen);
			innerArray[--size] = null;
			setLastReturned(-1);
			setIndex(nextIndex()-1);
			setCounter(getCounter()+1);
			globalCounter++;
		}
		
		protected int getCounter() {
			return counter;
		}

		protected void setCounter(int counter) {
			this.counter = counter;
		}
		
		protected int nextIndex() {
			return ind;
		}
		
		protected int previousIndex() {
			return ind-1;
		}

		protected void setIndex(int ind) {
			this.ind = ind;
		}

		protected int getLastReturned() {
			return lastReturned;
		}
		protected void setLastReturned(int lastReturned) {
			this.lastReturned = lastReturned;
		}
		
		
		
	}
	private class ListIteratorImpl extends IteratorImpl implements ListIterator{

		public ListIteratorImpl(){
			super();
		}
		public ListIteratorImpl(int ind) {
			if (ind<0){
				ind = 0;
			}else{
				if (ind>size){
					ind = size;
				}
			}
			setIndex(ind);
		}
		
		public void add(Object e) {
			if (globalCounter != getCounter()){
				throw new ConcurrentModificationException();
			}
			
			if (size == capacity){
				ensureCapacity(size*2);
			}
			int tailLen = size - nextIndex();
			System.arraycopy(innerArray, nextIndex(), innerArray, nextIndex() + 1, tailLen);
			innerArray[nextIndex()] = e;
			setIndex(nextIndex()+1);
			size++;
			setCounter(getCounter()+1);
			globalCounter++;
		}
		@Override
		public boolean hasPrevious() {
			return super.hasPrevious();
		}
		
		@Override
		public int nextIndex() {
			return super.nextIndex();
		}
		@Override
		public Object previous() {
			return super.previous();
		}
		@Override
		public int previousIndex() {
			return super.previousIndex();
		}
		
		public void set(Object o) {
			if (globalCounter != getCounter()){
				throw new ConcurrentModificationException();
			}
			if (getLastReturned() == -1){
				throw new IllegalStateException();
			}
			innerArray[getLastReturned()] = o;  
		}
		
	}
	
	
	public void ensureCapacity(int minCapacity){
		if (minCapacity<=size){
			return;
		}
		Object[] newArray = new Object[minCapacity];
		System.arraycopy(innerArray, 0, newArray, 0, size);
		innerArray = newArray;
		capacity = minCapacity;
	}
	
	public void trimToSize(){
		if (size == capacity){
			return;
		}
		
		Object[] newArray = new Object[size];
		System.arraycopy(innerArray, 0, newArray, 0, size);
		innerArray = newArray;
		capacity = size;
	}
	
	public void add(Object e) {
		ListIterator liter = listIterator(size);
		liter.add(e);
	}

	public void add(int index, Object e) {
		ListIterator liter = listIterator(index);
		liter.add(e);
	}

	public void addAll(Object[] c) {
		if (size+c.length>capacity){
			ensureCapacity(size+c.length);
		}
		System.arraycopy(c, 0, innerArray, size, c.length);
		size = size+c.length;
		globalCounter++;
	}

	public void addAll(int index, Object[] c) {
		if (size+c.length>capacity){
			ensureCapacity(size+c.length);
		}
		int tailLen = size - index;
		System.arraycopy(innerArray, index, innerArray, index + c.length, tailLen);
		System.arraycopy(c, 0, innerArray, index, c.length);
		size = size+c.length;
		globalCounter++;
	}


	public Object get(int index) {
		ListIterator liter = listIterator(index);
		return liter.next();
	}

	public Object remove(int index) {
		ListIterator liter = listIterator(index);
		Object obj = liter.next();
		liter.remove();
		return obj;
	}

	public void clear() {
		for (int i = 0; i<size;i++){
			innerArray[i]=null;
		}
		size = 0;
	}

	public boolean isEmpty() {
		return size > 0;
	}

	public void set(int index, Object e) {
		ListIterator liter = listIterator(index);
		liter.next();
		liter.set(e);
	}

	public int indexOf(Object o) {
		ListIterator iter = listIterator();
		while (iter.hasNext()){
			 if (o == iter.next()){
				 return iter.previousIndex();
			 }
		}
		return -1;
	}

	public int size() {
		return size;
	}

	public Object[] toArray() {
		Object[] newArray = new Object[size];
		System.arraycopy(innerArray, 0, newArray, 0, size);
		innerArray = newArray;
		return newArray;
	}

	public Iterator iterator() {
		return new IteratorImpl();
	}
	
	public ListIterator listIterator(){
		return new ListIteratorImpl();
	}
	
	public ListIterator listIterator(int ind){
		return new ListIteratorImpl(ind);
	}
	

	@Override
	public String toString() {
		String s = "[";
		StringBuilder sb = new StringBuilder(s);
		ListIterator liter = this.listIterator();
		if (!liter.hasNext()){
			return "[]";
		}
		while(liter.hasNext()){
			sb.append(liter.next().toString());
			sb.append(", ");
		}
		s = sb.substring(0, sb.length()-2)+"]";
		return s;
	}





}
